package com.wanrue.tcc.commom;

import com.wanrue.tcc.annotation.Tcc;
import com.wanrue.tcc.common.ContextService;
import com.wanrue.tcc.common.TccMethod;
import com.wanrue.tcc.context.Context;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Method;

public class DefaultContextService implements ContextService {

    @Override
    public Context get(Object... param) {
        if (param != null && param.length >= 2) {
            Method method = (Method) param[0];
            Object[] args = (Object[]) param[1];
            int position = position(method.getParameterTypes());
            if (position >= 0) {
                return (Context) args[position];
            }
        }
        return null;
    }

    @Override
    public void set(Context context, Object... param) {
        if (param != null && param.length >= 2) {
            Method method = (Method) param[0];
            Object[] args = (Object[]) param[1];
            int position = position(method.getParameterTypes());
            if (position >= 0) {
                args[position] = context;
            }
        }
    }

    @Override
    public TccMethod method(Object pjp, Object... args) {
        TccMethod method = defaultMethod((ProceedingJoinPoint) pjp);
        Context context = this.get(method.getMethod(), method.getArgs());
        method.setContext(context);
        return method;
    }

    int position(Class<?>[] parameterTypes) {
        int position = -1;
        for (int i = 0; i < parameterTypes.length; i++) {
            if (parameterTypes[i].equals(Context.class)) {
                position = i;
                break;
            }
        }
        return position;
    }

    protected TccMethod defaultMethod(ProceedingJoinPoint pjp) {
        TccMethod tccMethod = new TccMethod();
        Method method = ((MethodSignature) (pjp.getSignature())).getMethod();
        tccMethod.setMethod(method);
        tccMethod.setTarget(pjp.getTarget());
        tccMethod.setArgs(pjp.getArgs());
        tccMethod.setTargetClass(pjp.getTarget().getClass());
        tccMethod.setMethodName(method.getName());
        tccMethod.setParameterTypes(method.getParameterTypes());
        Tcc tcc = method.getAnnotation(Tcc.class);
        tccMethod.setConfirm(tcc.confirm());
        tccMethod.setCancel(tcc.cancel());
        tccMethod.setAsyncConfirm(tcc.asyncConfirm());
        tccMethod.setAsyncCancel(tcc.asyncCancel());
        tccMethod.setPropagation(tcc.propagation());
        return tccMethod;
    }
}
